/*
    This file is part of Raptor.

    Raptor is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    Raptor is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with Raptor.  If not, see <http://www.gnu.org/licenses/>.

 */

#include "rapidshare_com.h"

pluginHoster::pluginHoster()
{
    action = ACTION_NONE;
    step = 0;
    multipl = 1024;
    waitingId = -1;
    abort = false;
    cookie = "";
    lastSize = 0;

    waitTimer.setSingleShot(false);
    waitTimer.setInterval(1000);

    speedTimer.setSingleShot(false);
    speedTimer.setInterval(this->speedIntervall);

    loadCookie();

    this->_state = STATE_STOPPED;

    this->_info.allowOtherHostnames = false;
    this->_info.free.downloads = 0;
    this->_info.free.maxDownloads = 1;
    this->_info.free.maxConnections = 1;
    this->_info.premium.downloads = 0;
    this->_info.premium.maxDownloads = 0;
    this->_info.premium.maxConnections = 0;
    this->_info.host = "rapidshare.com";
    this->_info.name = "rapidshare.com";
    this->_info.reHost = QRegExp("^(.*\\.)?rapidshare\\.com$");
    this->_info.reLink = QRegExp("^(https?://)?([^\\s]*\\.)?rapidshare\\.com([^\\s]*)$");
    this->_info.mode = this->_mode;

    QSettings settings("raptor-loader", "Raptor");
    this->_info.maxDownloads = settings.value("hoster/rapidshare.com/maxDownloads", "0").toInt();

    connect(&http, SIGNAL(requestFinished(int, bool)), this, SLOT(slot_http_requestFinished(int, bool)));
    connect(&http, SIGNAL(readyRead(const QHttpResponseHeader &)), this, SLOT(slot_http_readyRead(const QHttpResponseHeader &)));
    connect(&http, SIGNAL(dataReadProgress(int, int)), this, SLOT(slot_http_dataReadProgress(int, int)));
    connect(&http, SIGNAL(sslErrors(const QList<QSslError> &)), this, SLOT(slot_http_sslErrors(const QList<QSslError> &)));

    connect(&speedTimer, SIGNAL(timeout()), this, SLOT(slot_speedTimer_timeout()));
    connect(&waitTimer, SIGNAL(timeout()), this, SLOT(slot_waitTimer_timeout()));
}

pluginHoster::~pluginHoster()
{
    //qDebug() << QString("~pluginHoster no. %1").arg(this->_id);
}

QObject* pluginHoster::clone()
{
    return new pluginHoster();
}

void pluginHoster::start()
{
    this->_progress = 0;
    this->_state = STATE_RUNNING;

    step = 0;
    waitingId = request(this->_url, "GET", QByteArray(), QMap<QString, QString>());
}

void pluginHoster::stop()
{
    if(this->_state != STATE_STOPPED)
    {
        abort = true;
        http.abort();
    }
}

void pluginHoster::login(QString const& user, QString const& pass)
{
    this->_mode = MODE_LOGIN;

    cookie = "";

    QByteArray post = "login=" + QUrl::toPercentEncoding(user) + "&password=" + QUrl::toPercentEncoding(pass) + "&uselandingpage=1";

    QUrl url("https://ssl.rapidshare.com/cgi-bin/premiumzone.cgi");

    QMap<QString, QString> headerData;

    headerData.insert("Referer", "https://ssl.rapidshare.com/premiumzone.html");
    headerData.insert("Content-Type", "application/x-www-form-urlencoded");

    waitingId = request(url, "POST", post, headerData);
}

void pluginHoster::getSize()
{
    this->_mode = MODE_GET_SIZE;

    cookie = "";

    waitingId = request(this->_url, "GET", QByteArray(), QMap<QString, QString>());
}

//private ->

int pluginHoster::request(QUrl const& url, QString const& method, QByteArray const& post, QMap<QString, QString> const& headerData, QIODevice *io)
{
    response = "";

    QHttpRequestHeader header(method, url.path());
    header.setValue("Host", url.host());


    QMap<QString, QString>::const_iterator it = headerData.constBegin();
    for(;it!=headerData.constEnd(); ++it)
    {
        header.setValue(it.key(), it.value());
    }

    if(this->cookie != "") header.setValue("Cookie", this->cookie);

    if(url.toString().left(5) == "https")
    {
        this->http.setHost(url.host(), QHttp::ConnectionModeHttps);
    } else
    {
        this->http.setHost(url.host());
    }

    if(io == 0)
    {
        return this->http.request(header, post);
    } else
    {
        speedTimer.start(this->speedIntervall);
        return this->http.request(header, post, io);
    }
}

void pluginHoster::loadCookie()
{
    QSettings settings("raptor-loader", "Raptor");

    //settings.beginGroup("hoster/rapidshare.com/settings");
    QString enc = settings.value("hoster/rapidshare.com/settings/enc", "").toString();
    //settings.endGroup();

    if(enc == "")
    {
        this->_mode = MODE_FREE;
        cookie = "";
    } else
    {
        this->_mode = MODE_PREMIUM;
        cookie = QString("enc=%1").arg(enc);

        this->_info.premium.maxDownloads = settings.value("hoster/rapidshare.com/maxDownloads", "0").toInt();
    }
}

QString pluginHoster::selectMirror(QStringList const& list)
{
    if(list.size() > 0)
    {
        return list.at(0);
    } else
    {
        return "";
    }
}

void pluginHoster::setError()
{
    this->_state = STATE_ERROR;
    emit statusMsg(this->_logId, this->_errorStr);

    //emit deleteMe(this->_id, true);
    switch(this->_mode)
    {
        case MODE_GET_SIZE:
            emit getSizeFinished(this->_id, true);
            break;

        case MODE_LOGIN:
            emit loginFinished(this->_id, true);
            break;

        case MODE_FREE:
            emit downloadFinished(this->_id, true);
            break;

        case MODE_PREMIUM:
            emit downloadFinished(this->_id, true);
            break;
    }
}

void pluginHoster::doLogin()
{
    if(this->http.lastResponse().hasKey("Set-Cookie"))
    {
        QString tmpString = this->http.lastResponse().value("Set-Cookie");

        QStringList tmpStringList = tmpString.split(";");

        for(int i=0; i<tmpStringList.size(); ++i)
        {
            QStringList item = tmpStringList.at(i).split("=");

            if(item.size() == 2)
            {
                if(item.at(0) == "enc")
                {
                    if(item.at(1).trimmed() != "")
                    {
                        QSettings settings("raptor-loader", "Raptor");

                        settings.setValue("hoster/rapidshare.com/settings/enc", item.at(1).trimmed());
                        emit loginFinished(this->_id, false);
                    }
                }
            }
        }

        emit loginFinished(this->_id, true);
    } else
    {
        setError();
    }
}

void pluginHoster::doGetSize()
{
    switch(step)
    {
        default:
        case 0:
        {
            if(page.indexOf(QRegExp("<!-- E#4 -->")) != -1)
            {
                this->_errorStr = tr("The file could not be found. Please check the download link.");
                setError();
            } else
            {
                QRegExp re("<form action=\"([^\"]*)\" method");
                if(re.indexIn(page) != -1)
                {
                    QUrl tmpUrl(re.cap(1));

                    QRegExp re2(">\\| (\\d+) (.)B</font></p>");
                    if(re2.indexIn(page) != -1)
                    {
                        quint64 tmpSize = re2.cap(1).toUInt();

                        QStringList sizeNames = QStringList() << " " << "K" << "M" << "G" << "T" << "P";

                        for(int i=0; i<sizeNames.count(); ++i)
                        {
                            if(re2.cap(2) == sizeNames.at(i))
                            {
                                double tmpDbl = tmpSize;

                                for(int o=0; o<i; ++o)
                                    tmpDbl = tmpDbl * multipl;

                                this->_fileSize = tmpDbl;
                            }
                        }

                    } else
                    {
                        this->_fileSize = 100*multipl*multipl;
                    }
                    emit getSizeFinished(this->_id, false);
                } else
                {
                    this->_errorStr = tr("Unknown error !!");
                    setError();
                }
            }

            break;
        }
    }
}

void pluginHoster::doPremium()
{
    switch(step)
    {
        default:
        case 0:
        {
            QRegExp reErrorX("<!-- E#(\\d+) --><p>([^<]*)</p>");
            if(reErrorX.indexIn(page) != -1)
            {
                this->_errorStr = reErrorX.cap(2);
                setError();
                return;
            } else
            {
                QRegExp reCookie("<input type=\"hidden\" name=\"dl\\.start\" value=\"PREMIUM\" />");
                if(reCookie.indexIn(page) != -1)
                {
                    this->_errorStr = tr("Wrong cookie, switching to free mode.");

                    emit changeMode(this->_id, MODE_FREE);

                    setError();
                    return;
                } else
                {
                    if(this->http.lastResponse().hasKey("Location"))
                    {
                        this->_url = QUrl(this->http.lastResponse().value("Location"));

                        if(!this->_downloadDir.exists(this->_folder)) this->_downloadDir.mkdir(this->_folder);

                        this->_downloadDir.cd(this->_folder);

                        while(this->_downloadDir.exists(this->_file))
                        {
                            emit statusMsg(this->_logId, tr("file exists, renaming."));
                            this->_file = "_" + this->_file;
                        }

                        this->_downloadFile = new QFile(this->_downloadDir.filePath(this->_file));
                        if(!this->_downloadFile->open(QIODevice::WriteOnly))
                        {
                            this->_errorStr = tr("Cant open file.");
                            setError();
                            return;
                        }

                        step = 1;
                        response = "";

                        this->_state = STATE_DOWNLOADING;

                        emit statusMsg(this->_logId, tr("Starting download."));

                        this->_progress = 0;
                        this->_downloadStart = QDateTime::currentDateTime();
                        this->_downloadEnd = this->_downloadStart;
                        this->_downloadEnd.addSecs(-1);

                        waitingId = request(this->_url, "GET", QByteArray(), QMap<QString, QString>(), this->_downloadFile);

                    } else
                    {
                        qDebug() << page;
                        this->_errorStr = tr("Location not found in header.");
                        setError();
                        return;
                    }
                }
            }
            break;
        }

        case 1:
        {
            speedTimer.stop();

            this->_downloadFile->close();
            this->_downloadEnd = QDateTime::currentDateTime();
            this->_state = STATE_FINISHED;

            int secs = this->_downloadStart.secsTo(this->_downloadEnd);

            if(secs > 0)
            {
                this->_speed = (double)this->_fileSize / secs;
            } else
            {
                this->_speed = this->_fileSize;
            }

            emit downloadFinished(this->_id, false);

            break;
        }
    }
}

void pluginHoster::doFree()
{
    switch(step)
    {
        default:
        case 0:
        {
            if(page.indexOf(QRegExp("<!-- E#4 -->")) != -1)
            {
                this->_errorStr = tr("The file could not be found. Please check the download link.");
                setError();
                return;
            } else
            {
                QRegExp re("<form action=\"([^\"]*)\" method");
                if(re.indexIn(page) != -1)
                {
                    QUrl tmpUrl(re.cap(1));

                    QRegExp re2(">\\| (\\d+) (.)B</font></p>");
                    if(re2.indexIn(page) != -1)
                    {
                        quint64 tmpSize = re2.cap(1).toUInt();

                        QStringList sizeNames = QStringList() << " " << "K" << "M" << "G" << "T" << "P";

                        for(int i=0; i<sizeNames.count(); ++i)
                        {
                            if(re2.cap(2) == sizeNames.at(i))
                            {
                                double tmpDbl = tmpSize;

                                for(int o=0; o<i; ++o)
                                    tmpDbl = tmpDbl * multipl;

                                this->_fileSize = tmpDbl;
                            }
                        }

                    } else
                    {
                        this->_fileSize = 100*multipl*multipl;
                    }

                    step = 1;

                    QByteArray post = "";
                    post.append("dl.start=Free");

                    waitingId = request(tmpUrl, "POST", post, QMap<QString, QString>());

                } else
                {
                    this->_errorStr = tr("Form not found !!");
                    setError();
                    return;
                }
            }

            break;
        }

        case 1:
        {
            QRegExp reSmallWait("var c=([^;]*);");
            QRegExp reBlockWait("Or try again in about (\\d*) minutes");
            QRegExp reSlotBlocked("<p>Your IP address ([\\.|\\d]*) is already downloading a file.  Please wait until the download is completed.</p>");
            QRegExp reSessionExpired("<p>The download session has expired. <a href=\"([^\"]*)\">Please click here to start the download again.</a></p>");
            QRegExp reServerFull("<b>Currently a lot of users are downloading files.  Please try again in (\\d*) minutes");
            QRegExp reNoSlots("We regret that currently we have no available slots for free users. Unfortunately you will have to wait (\\d*) minutes, this will allow us to continue to provide our service to our Premium users\\.");
            QRegExp reOverloaded("<p align=\"justify\">Unfortunately right now our servers are overloaded and we have no more download slots left for non-members. Of course you can also try again later.</p>");

            QRegExp reForm("<form action=\"([^\"]*)\" method");

            //todo:
            if(reSmallWait.indexIn(page) != -1)
            {
                int tmpTime = reSmallWait.cap(1).toInt();

                //<form name="dlf" action="http://rs138l34.rapidshare.com/files/347266214/9802029/raptor-0.17.tar.gz" method="post">
                //<input  type="radio" name="mirror" onclick="document.dlf.action=\'http://rs138l33.rapidshare.com/files/347266214/9802029/raptor-0.17.tar.gz\';" /> Level(3) #3<br />

                QStringList links;
                QRegExp reLink("<form name=\"dlf\" action=\"([^\"]*)\" method=\"post\">");
                if(reLink.indexIn(page) != -1)
                {
                    links.append(reLink.cap(1));
                }

                QRegExp reLinks("<input(?:[^>]*)onclick=\"document\\.dlf\\.action=\\\\'([^\\\\]*)\\\\';\" />([^<]*)<br />");
                int pos = 0;
                while((pos = reLinks.indexIn(page, pos)) != -1)
                {
                    links.append(reLinks.cap(1));
                    pos += reLinks.matchedLength();
                }

                this->_url = QUrl(selectMirror(links));

                if(this->_url.toString() == "")
                {
                    qDebug() << page;
                    this->_errorStr = tr("No download link found.");
                    setError();
                    return;
                }

                this->_state = STATE_WAITING;
                emit statusMsg(this->_logId, tr("Waiting %1 seconds.").arg(tmpTime));

                action = ACTION_DOWNLOAD;

                this->_timeLeft = tmpTime;
                wait = this->_timeLeft;

                waitTimer.start(1000);

            } else if(reBlockWait.indexIn(page) != -1)
            {
                int tmpTime = reBlockWait.cap(1).toInt();

                this->_timeLeft = tmpTime*60;
                wait = this->_timeLeft;

                action = ACTION_START;
                waitTimer.start(1000);

                this->_state = STATE_WAITING;
                emit statusMsg(this->_logId, tr("Try again in %1 minutes.").arg(tmpTime));

            } else if(reSlotBlocked.indexIn(page) != -1) //klappt
            {
                emit statusMsg(this->_logId, tr("IP (%1) is blocked.").arg(reSlotBlocked.cap(1)));

                this->_state = STATE_WAITING;

                this->_timeLeft = 60*5;
                wait = this->_timeLeft;

                action = ACTION_START;
                waitTimer.start(1000);

            } else if(reSessionExpired.indexIn(page) != -1)
            {
                emit statusMsg(this->_logId, tr("The download session has expired."));
                start();

            } else if(reServerFull.indexIn(page) != -1)
            {
                int tmpTime = reServerFull.cap(1).toInt();

                this->_state = STATE_WAITING;

                this->_timeLeft = tmpTime*60;
                wait = this->_timeLeft;

                action = ACTION_START;
                waitTimer.start(1000);

                emit statusMsg(this->_logId, tr("Currently a lot of users are downloading files. Please try again in %1 minutes").arg(tmpTime));
            } else if(reNoSlots.indexIn(page) != -1)
            {
                int tmpTime = reNoSlots.cap(1).toInt();

                emit statusMsg(this->_logId, tr("We regret that currently we have no available slots for free users. Unfortunately you will have to wait %1 minutes, this will allow us to continue to provide our service to our Premium users.").arg(tmpTime));
            } else if(reOverloaded.indexIn(page) != -1)
            {
                this->_state = STATE_WAITING;

                this->_timeLeft = 10;
                wait = this->_timeLeft;

                action = ACTION_START;
                waitTimer.start(1000);

                emit statusMsg(this->_logId, tr("Unfortunatly right now our servers are overloaded and we have no more download slots left for non-members. Of course you can also try again later."));
            } else
            {
                qDebug() << page;
                this->_errorStr = tr("Unknown error in step 1 !!");
                setError();
                return;
            }
            break;
        }

        case 2:
        {
            this->_downloadFile->close();
            speedTimer.stop();
            this->_downloadEnd = QDateTime::currentDateTime();

            if(this->_downloadFile->exists())
            {
                if(this->_downloadFile->size() < 20480)
                {
                    if(this->_downloadFile->open(QIODevice::ReadOnly | QIODevice::Text))
                    {
                        QString tmpStr = this->_downloadFile->readAll();
                        this->_downloadFile->close();

                        QRegExp re("<p>Your IP address (?:[\\.|\\d]*) is already downloading a file.  Please wait until the download is completed.</p>");
                        QRegExp re2("<p>The download session has expired. <a href=\"(?:[^\"]*)\">Please click here to start the download again.</a></p>");

                        if(re.indexIn(tmpStr) || re.indexIn(tmpStr))
                        {
                            this->_downloadFile->remove();
                            start();
                            return;
                        }


                    }
                }
            }

            this->_state = STATE_FINISHED;

            emit downloadFinished(this->_id, false);
            break;
        }
    }
}

void pluginHoster::setSpeed(double const& newSpeed)
{
    if(!speedList.isEmpty() && speedList.count() == this->smooth)
    {
        speedList.removeFirst();
        speedList.append(newSpeed);
    } else
    {
        speedList.append(newSpeed);
    }

    double tmpDbl = 0;

    for(int i=0; i<speedList.size(); ++i)
    {
        tmpDbl += speedList.at(i);
    }

    if(speedList.isEmpty())
        this->_speed = 0;
    else
        this->_speed = (tmpDbl / speedList.size());

    if(this->_speed < 1) this->_speed = 0;
}

//<- private


//slots ->
void pluginHoster::slot_http_requestFinished(int id, bool error)
{
    if(abort)
    {
        if(this->_downloadFile->isOpen()) this->_downloadFile->close();
        this->_errorStr = http.errorString();
        emit deleteMe(this->_id, error);
        return;
    }

    if(waitingId != id) return;
    page = QString(response);

    switch(this->_mode)
    {
        case MODE_FREE:
            doFree();
            break;

        case MODE_PREMIUM:
            doPremium();
            break;

        case MODE_GET_SIZE:
            doGetSize();
            break;

        case MODE_LOGIN:
            doLogin();
            break;
    }
}

void pluginHoster::slot_http_readyRead(const QHttpResponseHeader & resp)
{
    Q_UNUSED(resp);
    response.append(this->http.readAll());
}

void pluginHoster::slot_http_dataReadProgress(int done, int total)
{
    if(this->_state == STATE_DOWNLOADING)
    {
        this->_downloaded = done;
        this->_fileSize = total;

        if(this->_fileSize > 0) this->_progress = (int)((double)this->_downloaded * 100.0 / (double)this->_fileSize);
    }
}

void pluginHoster::slot_http_sslErrors(const QList<QSslError> & errors)
{
    //todo:
    Q_UNUSED(errors);
    this->http.ignoreSslErrors();
}

void pluginHoster::slot_speedTimer_timeout()
{
    if(speedTimer.interval() > 0) setSpeed((this->_downloaded - lastSize) / ((double)speedTimer.interval() / 1000.0));

    lastSize = this->_downloaded;

    if(this->_speed > 0) this->_timeLeft = (int)((double)(this->_fileSize - this->_downloaded) / this->_speed);
}

void pluginHoster::slot_waitTimer_timeout()
{
    if(this->_state == STATE_STOPPED) return;

    --this->_timeLeft;

    if(wait > 0) this->_progress = (int)(100 * ((double)(wait - this->_timeLeft) / wait));

    if(this->_timeLeft <= 0)
    {
        waitTimer.stop();

        switch(action)
        {
            case ACTION_NONE:

                break;

            case ACTION_START:

                start();
                break;

            case ACTION_DOWNLOAD:

                if(!this->_downloadDir.exists(this->_folder)) this->_downloadDir.mkdir(this->_folder);

                this->_downloadDir.cd(this->_folder);

                while(this->_downloadDir.exists(this->_file))
                {
                    emit statusMsg(this->_logId, tr("file exists, renaming."));
                    this->_file = "_" + this->_file;
                }

                this->_downloadFile = new QFile(this->_downloadDir.filePath(this->_file));
                if(!this->_downloadFile->open(QIODevice::WriteOnly))
                {
                    this->_errorStr = tr("Cant open file.");
                    setError();
                    return;
                }

                step = 2;
                this->_state = STATE_DOWNLOADING;

                emit statusMsg(this->_logId, tr("Starting download."));


                this->_progress = 0;
                this->_downloadStart = QDateTime::currentDateTime();
                this->_downloadEnd = this->_downloadStart;
                this->_downloadEnd.addSecs(-1);

                QByteArray post = "mirror=on";

                waitingId = request(this->_url, "POST", post, QMap<QString, QString>(), this->_downloadFile);
                break;
        }
    }
}

//<- slots

Q_EXPORT_PLUGIN2(rapidshare_com, pluginHoster);
